#ifndef CASSIOPEIALIBRARY_H
#define CASSIOPEIALIBRARY_H

#include <Windows.h>
#include <WindowsX.h>
#include <map>
#include <set>
#include <list>
#include <Dbt.h>
#include <windows.h>
#include <tchar.h>
#include <uxtheme.h>
#include <vssym32.h>
#include <shlobj.h>
#include <dwmapi.h>
#include <gdiplus.h>
#include <Psapi.h>
#include <fstream>
#include <stdio.h>

using namespace std;
using namespace Gdiplus;

#ifdef UNICODE
typedef wstring tstring;
#else
typedef string tstring;
#endif

#ifdef _WIN64
#define GCL_HBRBACKGROUND GCLP_HBRBACKGROUND
#define	GCL_HCURSOR GCLP_HCURSOR
#define	GCL_HICON GCLP_HICON
#define	GWL_HINSTANCE GWLP_HINSTANCE
#endif

#define RECTWIDTH(r)	((LONG)(r.right - r.left))
#define RECTHEIGHT(r)	((LONG)(r.bottom - r.top))
#define WPRIGHT(wp)		((LONG)(wp.x + wp.cx))
#define WPBOTTOM(wp)	((LONG)(wp.y + wp.cy))

#define PRECTWIDTH(r)	((LONG)(r->right - r->left))
#define PRECTHEIGHT(r)	((LONG)(r->bottom - r->top))
#define PWPRIGHT(wp)	((LONG)(wp->x + wp->cx))	
#define PWPBOTTOM(wp)	((LONG)(wp->y + wp->cy))		


class TStringSortTemplate
{
public:
	inline bool operator()(tstring ts1, tstring ts2) const
	{
		return _tcsicmp(ts1.c_str(),ts2.c_str()) < 0;
	}
};

struct Grid
{
	int Columns;
	int Rows;
};

class Utilities
{
public:
	enum DesktopWindow
	{
		DW_ProgMan,
		DW_SHELLDLL_DefViewParent,
		DW_SHELLDLL_DefView,
		DW_SysListView32
	};	
	enum FileType
	{
		FT_FILE=0x01,
		FT_DIRECTORY=0x02
	};	

	static HWND GetDesktopWindow(DesktopWindow desktopWindow);
	static bool IsValidTaskWindow(HWND hWnd, bool allowMinimized=true, bool allowMaximized=true);
	static bool IsSpecialWindow(HWND hWnd);
	static POINT GetCursorPosition();
	static HWND TopLevelWindow(HWND hWnd);
	static HWND TopLevelWindowFromMousePosition();
	static bool RectContainsPoint(RECT r, POINT p);
	static bool IsTopMost(HWND hWnd);
	static void SetTopMost(HWND hWnd);
	static void SetNoTopMost(HWND hWnd);
	static void ShowHWNDInfo(HWND hWnd);
	static BOOL KillProcess(DWORD pID);
	static RECT CalcWindowRect(HWND hWnd);
	static HWND GetWindowFromPointEx(POINT point);
	static void GetWindowTextEx(HWND hWnd, TCHAR* lpText);
	static void DrawDwmCompatibleText(RECT rect, HDC hdc, LPCWSTR text, int fontSize, COLORREF dwmDisabledTextColor, DWORD format, LONG fontWeight = FW_NORMAL, int glowSize=10, int xOffset=0, int yOffset=0);
	static void DrawDefaultText(RECT rect, HDC hdc, LPCWSTR text, int fontSize, COLORREF textColor, DWORD format, LONG fontWeight = FW_NORMAL, int xOffset = 0, int yOffset = 0);
	static bool EndsWith(LPCTSTR string, LPCTSTR end);
	static set<tstring, TStringSortTemplate> GetDirectoryEntries(LPCTSTR lpFileName, int ft, tstring specialDirExt=TEXT(""));
	static tstring GetKnownFolderPath(const KNOWNFOLDERID &rfid);
	static set<tstring, TStringSortTemplate> SplitTString(tstring str, tstring separator, bool ignoreEmptyStrings=false);
	static bool MoveWindowIntoVisibleArea(HWND hWnd);
	static POINT CenterScreen(SIZE windowSize, bool workingArea = true, bool primaryScreen = false);
	static POINT CenterScreen(LONG cx, LONG cy, bool workingArea = true, bool primaryScreen = false);
	static bool LockDrive(TCHAR cDrive, bool bLock);
	static Grid CalculateGridArea(int count);
	static void CreateRoundRectPath(GraphicsPath* path, RECT rect, int radius, int offset = 0);
	static void CreateRoundRectPath(GraphicsPath* path, RECT rect, int radiusTopLeft, int radiusTopRight, int radiusBottomRight, int radiusBottomLeft, int offset = 0);
	static void CreateRoundRectPath(GraphicsPath* path, RECT rect, int radiusTopLeft, int radiusTopRight, int radiusBottomRight, int radiusBottomLeft, int offsetLeft, int offsetTop, int offsetRight, int offsetBottom);
	static void DrawPNGFromResource(HINSTANCE hInstance, HDC hdc, LPCTSTR resourceName, int x, int y, int width, int height);
	static HICON GetSmallWindowIcon(HWND hWnd);
	static bool IsSpecialCassiopeiaWindow(HWND hWnd);
	static HWND GetLastValidTopLevelWindow();
	static tstring GetWindowFileName(HWND hWnd);
	static DWORD GetVolumeSerial(TCHAR volumeLetter);
	static tstring GetVolumeName(TCHAR volumeLetter);
	static TCHAR GetVolumeLetter(DWORD mask);
	static set<TCHAR> GetDrives(int driveType = -1);
	static bool ExistsFile(tstring fileName);
	static int GetEncoderClsid(tstring format, CLSID* pClsid);
	static bool IsWindowHanging(HWND hWnd);
	static tstring ConvertToString(int value);
private:
	static HWND HProgMan;
	static HWND HSHELLDLL_DefViewParent;
	static HWND HSHELLDLL_DefView;
	static HWND HSysListView32;

	static BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam);
};


class Window
{
public:
	typedef LRESULT (CALLBACK* WndProc)(LPVOID sender, HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

	Window();
	virtual ~Window();

	virtual void Create(HINSTANCE hInstance, LPCTSTR className = TEXT("CassiopeiaWindow"), WndProc wndProc = 0, DWORD exStyle = 0, DWORD style = WS_OVERLAPPEDWINDOW, UINT classStyle = 0, HBRUSH hbrBackground = 0, HCURSOR hCursor = 0, HICON hIcon = 0, HWND hWndParent = 0, HMENU hMenu = 0, LPVOID lpParam = 0);
	virtual void Close();

	HWND GetHWnd();

	virtual void Show();
	virtual void ShowNoActivate();
	virtual void Hide();
	virtual bool IsVisible();
	virtual bool IsTopMost();
	virtual void SetTopMost(bool value);
	virtual void SetScreenDocking(bool value);
	virtual bool GetScreenDocking();
	virtual void SetScreenDockingRange(int value);
	virtual int GetScreenDockingRange();
	virtual void SendToBottom();
	virtual void BringOnTop();
	virtual tstring GetText();
	virtual void SetText(tstring text);
	virtual void SetIcon(HICON hIcon);
	virtual HICON GetIcon();
	virtual void SetBackgroundBrush(HBRUSH hBrush);
	virtual HBRUSH GetBackgroundBrush();
	virtual void SetCursor(HCURSOR hCursor);
	virtual HCURSOR GetCursor();
	virtual RECT GetBounds();
	virtual void SetBounds(RECT value, bool force = false);
	virtual void SetBounds(LONG left, LONG top, LONG right, LONG bottom, bool force = false);
	virtual SIZE GetSize();
	virtual void SetSize(SIZE value, bool force = false);
	virtual void SetSize(LONG cx, LONG cy, bool force = false);
	virtual POINT GetLocation();
	virtual void SetLocation(POINT value);
	virtual void SetLocation(LONG x, LONG y);	
	virtual void Refresh();	
	virtual void SetTag(LPVOID tag);
	virtual LPVOID GetTag();

protected:
	HINSTANCE hInstance;
	WndProc wndProc;
	LPVOID tag;

	static Window* GetInstance(HWND hWnd);
	static map<HWND, Window*>::iterator GetInstancesBegin();
	static map<HWND, Window*>::iterator GetInstancesEnd();

private:
	HWND hWnd;
	bool screenDocking;
	int screenDockingRange;

	static Window* tmpInstance;
	static map<HWND, Window*> instances;
	
	static LRESULT CALLBACK BaseWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
};

class CustomWindow : public Window
{
public:
	CustomWindow();
	virtual ~CustomWindow();

	virtual void Create(HINSTANCE hInstance, LPCTSTR className, WndProc ptr = 0, DWORD exStyle = 0, DWORD style = WS_OVERLAPPEDWINDOW, UINT classStyle = 0, HBRUSH hbrBackground = 0, HCURSOR hCursor = 0, HICON hIcon = 0, HWND hWndParent = 0, HMENU hMenu = 0, LPVOID lpParam = 0);
	void SetFrameMargins(int cxLeftWidth, int cxRightWidth, int cyTopHeight, int cyBottomHeight);
	void SetFrameMargins(int all);
	void SetFrameMargins(MARGINS m);
	MARGINS GetFrameMargins();
	void SetGlassMargins(int cxLeftWidth, int cxRightWidth, int cyTopHeight, int cyBottomHeight);
	void SetGlassMargins(int all);
	void SetGlassMargins(MARGINS m);
	MARGINS GetGlassMargins();

protected:
	MARGINS frameMargins;
	MARGINS glassMargins;

private:	

	WndProc wndProcPtr;


	static LRESULT HitTestNCA(HWND hWnd, WPARAM wParam, LPARAM lParam, MARGINS margins);
	static LRESULT CustomCaptionProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool* pCancel);
	static LRESULT CALLBACK WndProc(LPVOID sender, HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
};

class RegisterDriveWindow : public Window
{
public:
	typedef void (*RDWNDEVENTPROC)(RegisterDriveWindow* sender, TCHAR driveLetter, DWORD driveSerial, LPVOID parent, LPVOID tag);

	RegisterDriveWindow(LPVOID parent = 0);
	~RegisterDriveWindow();
	void Create(HINSTANCE hInstance, HICON hIcon = 0);
	void Show();

	void DrivePluggedIn(RDWNDEVENTPROC ptr);
private:
	RDWNDEVENTPROC drivePluggedIn;
	LPVOID parent;

	static LRESULT CALLBACK WndProc(LPVOID sender, HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

};


class BinaryFileHelper
{
public:
	BinaryFileHelper(const char* fileName, ios_base::openmode openMode);
	~BinaryFileHelper();

	__CLR_OR_THIS_CALL operator void *() const;
	bool __CLR_OR_THIS_CALL operator!() const;

	void Write(const char* data, streamsize size);
	void Read(char* data, streamsize size);

	void WriteString(tstring str);
	tstring ReadString();

	void Flush();
	void Close();
	void Exceptions(ios_base::iostate newExceptions);
private:
	fstream* fileStream;
	bool closed;
};


typedef bool (*ENUMDESKICONPROC)(HWND desktopHWnd, RECT desktopBounds, int itemIndex, LPCTSTR itemText, POINT itemPosition, RECT iconBounds, SIZE gridSize, bool first, bool last, LPARAM lParam1, LPARAM lParam2);

struct DesktopItem
{
	int Index;
	tstring Text;
	POINT Location;
	RECT Bounds;
};

struct DesktopInfo
{
	HWND HWnd;
	RECT Bounds;
	SIZE GridSize;
};

class DesktopItemSortTemplate
{
public:
	inline bool operator()(DesktopItem di1, DesktopItem di2) const
	{
		//return di1.Index < di2.Index;
		//alphabetical
		return 	_tcsicmp(di1.Text.c_str(),di2.Text.c_str()) < 0;
	}
};

class DesktopHelper
{
public:
	
	DesktopHelper(bool refreshDesktopItems = false);
	~DesktopHelper();
	bool GetDesktopAlignToGrid();
	bool GetDesktopAutoArrange();
	void SetDesktopAlignToGrid(bool value);
	void SetDesktopAutoArrange(bool value);
	void RefreshDesktop();
	int GetDesktopItemCount();

	DesktopInfo DesktopInfo;
	set<DesktopItem, DesktopItemSortTemplate> DesktopItems;
	void RefreshDesktopItems();

private:
	HANDLE hProcessDesktop;
	int * desktopVMPtr;

};


class PaintedButton
{
public:
	enum GradientMode
	{
		Vertical,
		Horizontal
	};

	typedef void (*PBEVENTPROC)(PaintedButton* sender, LPVOID tag);

	PaintedButton(HWND parentWindow);
	~PaintedButton();

	bool HandleWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
	void HandlePainting(HDC hdc);
	
	void Clicked(PBEVENTPROC ptr);
	void MouseLeave(PBEVENTPROC ptr);
	void MouseEnter(PBEVENTPROC ptr);
	void RightMouseButtonUp(PBEVENTPROC ptr);
	void Hide();
	void Show();
	bool IsVisible();
	HWND GetParentWindow();
	tstring GetText();
	void SetText(tstring text, int leftPadding = 0, int topPadding = 0, int rightPadding = 0, int bottomPadding = 0);
	DWORD GetTextFormat();
	void SetTextFormat(DWORD format);
	COLORREF GetTextColor();
	void SetTextColor(COLORREF cr);
	int GetFontSize();
	void SetFontSize(int size);
	int GetFontWeight();
	void SetFontWeight(LONG weight);
	GradientMode GetGradientMode();
	void SetGradientMode(GradientMode mode);
	RECT GetBounds();
	void SetBounds(RECT value);
	void SetBounds(LONG left, LONG top, LONG right, LONG bottom);
	SIZE GetSize();
	void SetSize(SIZE value);
	void SetSize(LONG cx, LONG cy);
	POINT GetLocation();
	void SetLocation(POINT value);
	void SetLocation(LONG x, LONG y);	
	LPVOID GetTag();
	void SetTag(LPVOID lpVoid);
	bool GetCheckable();
	void SetCheckable(bool value);
	bool GetChecked();
	void SetChecked(bool value);
	void PerformClick();
	void Refresh();

private:
	void CallPBEvent(PBEVENTPROC ptr);

	PBEVENTPROC onClickPtr;
	PBEVENTPROC onMouseLeavePtr;
	PBEVENTPROC onMouseEnterPtr;
	PBEVENTPROC onRightMouseButtonUpPtr;
	COLORREF textColor;
	HWND parentHWnd;
	RECT bounds;
	tstring text;
	bool visible;
	bool checkable;
	bool buttonHover;
	bool buttonChecked;
	GradientMode gradientMode;
	LPVOID tag;
	RECT textPadding;
	DWORD textFormat;
	DWORD fontSize;
	DWORD fontWeight;
};


class ScreenThumbnailHelper
{
public:
	ScreenThumbnailHelper(HWND hWnd);
	~ScreenThumbnailHelper();


	//Image* CreateScreenshot(set<HWND> windowsToInclude, POINT offset, RECT rect);
	void CreateSimulatedScreenThumbnail(set<HWND> windowsToInclude, POINT screenOffset, RECT rect);
	void DestroySimulatedScreenThumbnail();
private:
	void RefreshScreenThumbnailAreaData(RECT rect);

	HWND hWnd;
	set<HWND> windowsToInclude;
	list<HWND> windows;
	list<HTHUMBNAIL> hThumbnails;
	double FactorX;
	double FactorY;

	//Image* CaptureWindow(HWND hWnd);
	//Image* CaptureWindow(HWND hWnd, Size s);

	static int virtualscreenXOffset;
	static int virtualscreenYOffset;
	static map<HWND, ScreenThumbnailHelper*> instances;


	static BOOL CALLBACK EnumDisplayMonitorsProc(HMONITOR hMonitor, HDC hdc, LPRECT lpRect, LPARAM lParam);
	static BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam);
};


class SimpleTimer
{
public:
	typedef VOID (CALLBACK* TickProc)(SimpleTimer* sender, HWND hWnd, UINT_PTR idEvent, DWORD dwTime);
	
	SimpleTimer(HWND hWnd = 0);
	~SimpleTimer();

	void Start();
	void Stop();
	void SetInterval(UINT interval);
	UINT GetInterval();
	bool IsRunning();
	void SetTag(LPVOID tag);
	LPVOID GetTag();
	void Tick(TickProc ptr);
private:
	LPVOID tag;
	HWND hWnd;
	UINT timerID;
	UINT interval;
	bool running;
	TickProc onTickPtr;

	static map<UINT, SimpleTimer*> instances;
	static UINT instanceID;
	static VOID CALLBACK TimerProc(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
};


class ThumbnailHelper
{
public:
	static HTHUMBNAIL CreateThumbnail(HWND hWndDestination, HWND hWndSource, RECT rect);
	static void DestroyThumbnail(HTHUMBNAIL hThumbnail);
};


enum CCState
{
	NoActionNecessary = 0, 
	NotReady = -1,
	Successful = 1
};

class CassiopeiaComponent
{
public:
	typedef void (*ACTIVECHANGEDPROC)(CassiopeiaComponent* sender, bool active);

	CassiopeiaComponent();
	virtual ~CassiopeiaComponent();
	virtual CCState Activate();
	virtual CCState Deactivate();
	bool IsActive();
	void ToggleActiveState();
	void ActiveChanged(ACTIVECHANGEDPROC ptr);
protected:
	virtual void OnActiveChanged();

	bool active;	
	ACTIVECHANGEDPROC activeChangedPtr;
};


#endif